package numerics;

import java.util.List;


public class LinearRegression {
	private double r;
	private double xAvg;
	private double yAvg;
	private double tailArea;
	private double betaZero;
	private double betaOne;
	private double xk;
	private double yk;
	private double range;
	private double UPI;
	private double LPI;
	
	List<double[]> data;

	public LinearRegression(List<double[]> list, double xk) {
		data = list;

		r = this.calcR();
		xAvg = this.calcXAvg();
		yAvg = this.calcYAvg();
		tailArea = this.calcTailArea();
		betaOne = this.calcBetaOne();
		betaZero = this.calcBetaZero();
		this.xk = xk;
		yk = this.calcYk();
		range = this.calcRange();
		UPI = this.calcUPI();
		LPI = this.calcLPI();
	}


	private double calcR() {
		int n = data.size();
		
		double numerator = n * Sum.sumXMultY(data) - Sum.sumX(data, 0) * Sum.sumX(data, 1);
		double denominator = Math.sqrt((n * Sum.sumX2Pow(data, 0, 2) - Math.pow(
								Sum.sumX(data, 0), 2))
						* (n * Sum.sumX2Pow(data, 1, 2) - Math.pow(
								Sum.sumX(data, 1), 2)));
		
		return numerator / denominator;
	}

	private double calcXAvg() {
		return Sum.sumX(data, 0) / (double) data.size();
	}

	private double calcYAvg() {
		return Sum.sumX(data, 1) / (double) data.size();
	}

	private double calcTailArea() {
		int n = data.size();
		double x = Math.abs(r) * Math.sqrt((n - 2) / (1 - Math.pow(r, 2)));

		double p = (new TStudentDistribution(n - 2)).calcPGivenX(x);

		return 2.0 * (1.0 - p);
	}

	private double calcBetaZero() {
		return yAvg - betaOne * xAvg;
	}

	private double calcBetaOne() {
		int n = data.size();

		double numerator = Sum.sumXMultY(data) - (n * xAvg * yAvg);
		double denominator = Sum.sumX2Pow(data, 0, 2.0)
				- (n * Math.pow(xAvg, 2.0));
		
		return numerator / denominator;
	}

	private double calcRange() {
		int n = data.size();
		double x = (new TStudentDistribution(n - 2)).calcXGivenP(0.5 + 0.7 / 2);
		double temp_sum1 = 0.0, temp_sum2 = 0.0;

		for (int i = 0; i < n; ++i) {
			temp_sum1 += Math.pow(
					data.get(i)[1] - betaZero - betaOne * data.get(i)[0], 2.0);
			temp_sum2 += Math.pow(data.get(i)[0] - xAvg, 2.0);
		}

		double sigma = Math.sqrt((1.0 / (n - 2.0)) * temp_sum1);

		return x * sigma * Math.sqrt(
						1.0 + 1.0 / (double) n
						+ (Math.pow(xk - xAvg, 2.0) / temp_sum2)
		);
	}

	private double calcUPI() {
		return yk + range;
	}

	private double calcLPI() {
		return yk - range;
	}

	private double calcYk() {
		return betaZero + betaOne * xk;
	}
	

	public double getR() {
		return r;
	}

	public double getTailArea() {
		return tailArea;
	}

	public double getBetaZero() {
		return betaZero;
	}

	public double getBetaOne() {
		return betaOne;
	}

	public double getYk() {
		return yk;
	}

	public double getUPI() {
		return UPI;
	}

	public double getLPI() {
		return LPI;
	}

}
